package com.cgs.query.server.config

import com.cgs.query.configuration.ConfigProperties
import com.cgs.query.configuration.QueryConfiguration
import com.cgs.query.connection.ConnectionFactory
import com.cgs.query.executor.Executor
import com.cgs.query.handler.*
import com.cgs.query.handler.executor.JdbcExecutor
import com.cgs.query.handler.render.MultipleParameterHandler
import com.cgs.query.handler.result.MapResultSetHandler
import com.cgs.query.handler.security.SecurityAspect
import com.cgs.query.metrics.SERVER_POOL_NAME
import com.cgs.query.metrics.ServerDataSourceMetrics
import com.cgs.query.server.aspect.ExecuteAspect
import com.cgs.query.server.connection.ServerPoolConnectionFactory
import com.cgs.query.server.handler.ServerSecurityHandler
import com.cgs.query.server.listener.Project2DefineListener
import com.cgs.query.server.listener.QueryDefineListener
import com.cgs.query.server.listener.QueryProjectListener
import com.cgs.query.server.listener.QuerySourceListener
import com.cgs.query.server.service.ServerQueryService
import com.cgs.query.service.IQueryProjectService
import com.cgs.query.service.IQueryService
import com.cgs.query.service.IQuerySourceService
import com.cgs.query.util.Utils
import com.sucsoft.kotlin.annotation.AllOpen
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import mu.KotlinLogging
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.ApplicationListener
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Import
import org.springframework.context.event.ContextRefreshedEvent
import org.springframework.scheduling.annotation.EnableAsync
import java.util.concurrent.atomic.AtomicBoolean
import javax.sql.DataSource

private val logger = KotlinLogging.logger { }

@Configuration
@AllOpen
@ComponentScan("com.cgs.query.server.controller")
@EnableAsync
@Import(SecurityAspect::class, ExecuteAspect::class)
open class ServerConfiguration : ApplicationListener<ContextRefreshedEvent> {

    @Bean
    @ConditionalOnMissingBean(UserInfoHolder::class)
    open fun defaultUserInfoHolder(): UserInfoHolder {
        return object : UserInfoHolder {
            override fun getUserId(): String = "admin"
        }.apply { logger.info { "生成默认的用户管理,用户id:admin" } }
    }

    @Bean
    @ConditionalOnMissingBean(ResultSetHandler::class)
    open fun defaultResultSetHandler(): ResultSetHandler = MapResultSetHandler().apply { logger.info { "创建MapResultSetHandler" } }

    @Bean
    @ConditionalOnMissingBean(ParameterHandler::class)
    open fun multipleParameterHandler(): ParameterHandler = MultipleParameterHandler().apply { logger.info { "创建MultipleParameterHandler" } }

    @Bean
    @ConfigurationProperties(prefix = "sql.runner")
    open fun configProperties(): ConfigProperties = ConfigProperties()

    @Bean
    @ConditionalOnMissingBean(SecurityHandler::class)
    @ConditionalOnBean(SecurityAspect::class)
    open fun defaultServerSecurityHandler(projectService: IQueryProjectService): SecurityHandler =
            ServerSecurityHandler(projectService).apply { logger.info { "创建ServerSecurityHandler,进行权限检查" } }

    @Bean
    @ConditionalOnMissingBean(QueryConfiguration::class)
    open fun defaultConfiguration(parameterHandler: ParameterHandler, resultSetHandler: ResultSetHandler
                                  , @Autowired(required = false) securityHandler: SecurityHandler?): QueryConfiguration =
            QueryConfiguration(configProperties(), parameterHandler, resultSetHandler).apply {
                this.securityHandler = securityHandler
                logger.info { "创建Configuration" }
            }


    @Bean()
    @ConfigurationProperties(prefix = "sql.runner.datasource")
    open fun hikariConfig(): HikariConfig = HikariConfig()

    @Bean("sql.runner.queryDS")
    open fun queryDataSource2(): DataSource {
        return HikariDataSource(hikariConfig().apply {
            this.poolName = SERVER_POOL_NAME
            this.metricRegistry = ServerDataSourceMetrics.INSTANCE.registry
        })
    }
/*
    @Bean("sql.runner.queryDS")
    @ConfigurationProperties(prefix = "sql.runner.datasource")
    open fun queryDataSource(): DataSource = DataSourceBuilder.create().build().apply { logger.info { "创建DataSource" } }*/

    @Bean
    open fun serverConnectionFactory(querySourceService: IQuerySourceService): ConnectionFactory =
            ServerPoolConnectionFactory(querySourceService).apply { logger.info { "创建ServerPoolConnectionFactory" } }

    @Bean
    @ConditionalOnMissingBean(Executor::class)
    open fun defaultServerJdbcExecutor(config: QueryConfiguration, connectionFactory: ConnectionFactory): Executor =
            JdbcExecutor(config, connectionFactory).apply { logger.info { "创建ServerJdbcExecutor" } }

    @Bean
    @ConditionalOnMissingBean(IQueryService::class)
    open fun defaultServerQuasarService(executor: Executor, defineFindHandler: DefineFindHandler, sourceFindHandler: SourceFindHandler) =
            ServerQueryService(executor, defineFindHandler, sourceFindHandler).apply { logger.info { "创建ServerQueryService" } }

    @Bean
    open fun queryDefineListener() = QueryDefineListener()

    @Bean
    open fun queryProjectListener() = QueryProjectListener()

    @Bean
    open fun querySourceListener() = QuerySourceListener()

    @Bean
    open fun project2DefineListener() = Project2DefineListener()

    override fun onApplicationEvent(event: ContextRefreshedEvent?) {
        val applicationContext = event!!.applicationContext
        Utils.nullAndThen(applicationContext.parent) {
            val userInfoHolderAwareMap = applicationContext!!.getBeansOfType(UserInfoHolderAware::class.java)
            val userInfoHolder = applicationContext.getBean(UserInfoHolder::class.java)
            userInfoHolderAwareMap.forEach { _, bean -> bean.setUserInfoHolder(userInfoHolder) }
        }
    }
}

